/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package pope.jloading.downloader;

import pope.jloading.Elog;
import pope.jloading.TaskManager;
import pope.jloading.TaskModel;
import softutil.UnInstall;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;

import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.util.cmd.MyCMD;

import exchange4Server.Messager4Server;

public class Downloader {
    private Config config;  // 状态保存配置文件
    private String taskid;      // 任务标识
    private String name;    // 短文件名(不包含后缀及路径)
    private String url;     // 下载源地址
    private File fileSave;  // 保存路径
    private File fileProcess;//正在下载中的文件名(默认比原文件名多带一个"_")
    private int threads;    // 下载的线程数
    
    private boolean isFirst = true;    // 是否为首次下载（需要创建状态文件）
    private boolean isInit = false;     // 是否已经初始化.
    private long totalBytes;// 总文件大小
    private long readBytes; // 已经读取的大小
    private long readBytesNow;  // 当次启动后所读的字节数
    
    // 待完成的任务列表
    private Vector<Piece> tasks = new Vector<Piece>();
    // 当前的所有任务列表，包括正在处理中的与待完成的任务列表
    private Vector<Piece> tasksAll = new Vector<Piece>();
    
    // 空闲的任务线程
    private Vector<PieceLoader> frees = new Vector<PieceLoader>();
    // 所有执行线程,包括执行的与空闲的
    private List<PieceLoader> loaders = new ArrayList<PieceLoader>();
    
    // 运行状态信息
    private int state = 0;
	private String sid;
	private String action;
	private Document doc;
	private File tasksFile;
    private final static int STATE_NONE = 0;
    private final static int STATE_READY = 1;
    private final static int STATE_LOADING = 2;
    private final static int STATE_PAUSED = 3;
    private final static int STATE_STOPPED = 4;
    public final static int STATE_OK = 5;
    public void setState(int state) {
		this.state = state;
	}
    public int getState() {
		return state;
	}
    
    // -------------------------------------------------- 构造
    
    /**
     * 使用状态文件进行断点续传
     * @param config
     */
    public Downloader(Config config) {
        this.config = config;
        
        taskid = config.getId();
        name = config.getName();
        url = config.getURLs().get(0);
        fileSave = new File(config.getSave());
        threads = config.getThreads();
        totalBytes = config.getLength();    // 1 文件长度
        
        sid=config.getSid();//add by pope
        
        List<Piece> pieces = config.loadPieces();   // 2 任务列表
        //System.out.println("pieces=" + pieces.size());
        for (Piece p : pieces) {
            tasksAll.add(p);
            tasks.add(p);
        }
        // 初始化已读文件的长度，将文件长度减去未读的文件长度，即是已读的长度
        for (Piece p : tasksAll) {
            readBytes += (p.getPos() - p.getStart());
        }
        readBytes += pieces.size();
//        System.out.println("readBytes=" + readBytes);
        // 标识为“已初始化”“续传操作(因有Config文件)”
        isInit = true;
        isFirst = false;
        state = STATE_READY;
    }
    
    /**
     * 新建任务方式进行下载
     * @param name
     * @param url
     * @param save
     * @param threads
     * @throws java.lang.Exception
     */
    public Downloader(String taskid, String name, String url, String save, int threads) {
        this.taskid = taskid;
        this.name = name;
        this.url = url;
        this.fileSave = new File(save);
        this.threads = threads;
        new Thread(new Runnable(){
            public void run() {
                init0();
            }
        }).start();
    }
    /**
     * 
     * @param taskid
     * @param name
     * @param url
     * @param save
     * @param threads
     * @param sid
     */
    public Downloader(String taskid, String name, String url, String save, int threads,String sid,String action) {
        this.taskid = taskid;
        this.name = name;
        this.url = url;
        this.fileSave = new File(save);
        this.threads = threads;
        this.sid=sid;
        this.action=action;
        new Thread(new Runnable(){
            public void run() {
                init0();
            }
        }).start();
    }

    /** 首次下载文件(即非使用Config文件的方式)，需要初始化相关信息 */
    private void init0() {
        // 对文件进行分块
        try {
            totalBytes = new URL(url).openConnection().getContentLength();
            if (totalBytes == -1) state = STATE_NONE;
        } catch (IOException ioe) {
            return;
        }
        // 创建分块，并创建相应的负责下载的线程
        long pieceSize = (long) Math.ceil((double) totalBytes / (double) threads);
        long pStart = 0;
        long pEnd = 0;
        tasksAll.clear();
        tasks.clear();
        for (int i = 0; i < threads; i++) {
            if (i == 0) {
                pStart = pieceSize * i;
            }
            if (i == threads - 1) {
                pEnd = totalBytes;
            } else {
                pEnd = pStart + pieceSize;
            }
            Piece piece = new Piece(pStart, pStart, pEnd);
            tasksAll.add(piece);
            tasks.add(piece);
            pStart = pEnd + 1;
        }
        // 标识为“已初始化”“首次下载”
        isInit = true;
        isFirst = true;
        state = STATE_READY;
    }
    
    // 创建状态保存文件
    private synchronized void checkAndCreateConfig() {
        int n = 0;
        try {
            // 判断是否为首次下载，如果是，则需要判断是否存在同名的文件，若存在同
            // 名的文件，则重新更新保存路径
            if (isFirst) {
                String path = fileSave.getAbsolutePath();
                String f_head = path.substring(0, path.lastIndexOf("."));
                String f_suffix = path.substring(path.lastIndexOf(".")); // 含"."
                File f1 = new File(path);               // 最终保存文件
                File f2 = new File(path + "_");         // 未完成任务之前的文件名
                File f3 = new File(path + "_config");   // 状态保存文件
                // 检查并确保不存在相同名称的文件名，saveChange标识（存在相同的文件名)
                while (f1.exists() || f2.exists() || f3.exists()) {
                    n++;
                    path = f_head + "[" + n + "]" + f_suffix;
                    f1 = new File(path);
                    f2 = new File(path + "_");
                    f3 = new File(path + "_config");
                }
                try {
                    f2.createNewFile();
                } catch (Exception e) {
                }
                fileSave = f1;
                fileProcess = f2;
                isFirst = false;   
                // 创建状态文件，并保存,字段分别为
                // 任务标识, 文件名，下载源，保存路径，文件大小， 线程数
//                config = ConfigFactory.createConfig(
//                        taskid, name, url, fileSave.getAbsolutePath(), totalBytes, threads);
                config = ConfigFactory.createConfig(
                      taskid, name, url, fileSave.getAbsolutePath(), totalBytes, threads,sid,action);
                config.savePieces(tasksAll);
                // 更新任务列表中的信息
                TaskManager.getInstance().updateConfigPath(config);
            } else {
                fileProcess = new File(fileSave.getAbsolutePath() + "_");
            }
        } catch (Exception e) {
            Elog.log("出错：创建配置文件是遇到问题!" + getClass().getName());
        }
    }
    
    /**
     * 检查文件 覆盖文件
     *
     */
    private synchronized void checkAndCreateConfigAndOverRide() {
        int n = 0;
        try {
            // 判断是否为首次下载，如果是，则需要判断是否存在同名的文件，若存在同
            // 名的文件，则重新更新保存路径
            if (isFirst) {
                String path = fileSave.getAbsolutePath();
                String f_head = path.substring(0, path.lastIndexOf("."));
                String f_suffix = path.substring(path.lastIndexOf(".")); // 含"."
                File f1 = new File(path);               // 最终保存文件
                File f2 = new File(path + "_");         // 未完成任务之前的文件名
                File f3 = new File(path + "_config");   // 状态保存文件
                // 检查并确保不存在相同名称的文件名，saveChange标识（存在相同的文件名)
                while (f1.exists() || f2.exists() || f3.exists()) {
//                    n++;
//                    path = f_head + "[" + n + "]" + f_suffix;
                	//TODO 这里造成了死机 如何解决？ 20080808                           	
                	
                    f1 = new File(path);
                    f2 = new File(path + "_");
                    f3 = new File(path + "_config");
                }
                try {
                    f2.createNewFile();
                } catch (Exception e) {
                }
                fileSave = f1;
                fileProcess = f2;
                isFirst = false;   
                // 创建状态文件，并保存,字段分别为
                // 任务标识, 文件名，下载源，保存路径，文件大小， 线程数
//                config = ConfigFactory.createConfig(
//                        taskid, name, url, fileSave.getAbsolutePath(), totalBytes, threads);
                config = ConfigFactory.createConfig(
                      taskid, name, url, fileSave.getAbsolutePath(), totalBytes, threads,sid,action);
                config.savePieces(tasksAll);
                // 更新任务列表中的信息
                TaskManager.getInstance().updateConfigPath(config);
            } else {
                fileProcess = new File(fileSave.getAbsolutePath() + "_");
            }
        } catch (Exception e) {
            Elog.log("出错：创建配置文件是遇到问题!" + getClass().getName());
        }
    }
    
    private synchronized void checkAndCreateConfigNew() {
        try {
            // 判断是否为首次下载，如果是，则需要判断是否存在同名的文件，若存在同
            // 名的文件，则重新更新保存路径
            if (isFirst) {
                String path = fileSave.getAbsolutePath();
                String f_head = path.substring(0, path.lastIndexOf("."));
                String f_suffix = path.substring(path.lastIndexOf(".")); // 含"."
                File f1 = new File(path);               // 最终保存文件
                File f2 = new File(path + "_");         // 未完成任务之前的文件名
                File f3 = new File(path + "_config");   // 状态保存文件
                // 检查并确保不存在相同名称的文件名，saveChange标识（存在相同的文件名)
               if(f1.exists() || f2.exists() || f3.exists()) {
                	
                	int option = JOptionPane.showConfirmDialog(null, "你存在下载文件，是否进行切换序列号", "提示",JOptionPane.YES_NO_OPTION);
    				if (option == JOptionPane.YES_OPTION){
    					System.out.println("this is OK Action");
    				}else{
    					System.out.println("不进行操作");
    				}
                	
                }
               
                fileSave = f1;
                fileProcess = f2;
                isFirst = false;   
                
            } else {
                fileProcess = new File(fileSave.getAbsolutePath() + "_");
                state = STATE_LOADING;
                loading();
            }
            
           
            
        } catch (Exception e) {
            Elog.log("出错：创建配置文件是遇到问题!" + getClass().getName());
        }
    }
      
    private void loading() {
        // 初始化下载线程(先清除，以确保没有不可用的线程)
        loaders.clear();
        for (int i = 0; i < threads; i++) {
            PieceLoader pl = new PieceLoader(this, tasks);
            pl.start();
            loaders.add(pl);
        }        
        // 初始化定时器,每10秒保存一次状态
        timer = new Timer();
        timer.schedule(new MyTimerTask(), 0, 1000 * 5);//old is 10s,now is 5s
    }
    
    // ------------------------------------------------------------ 状态信息
    /** 状态未知 */
    public boolean isNone() {
        return state == STATE_NONE ? true : false;
    }
    
    /** 是否运行中的 */
    public boolean isLoading() {
        return state == STATE_LOADING ? true : false;
    }
    
    /** 是否准备就绪的 */
    public boolean isReady() {
        return state == STATE_READY ? true : false;
    }
    
    /** 是否暂停中的 */
    public boolean isPaused() {
        return state == STATE_PAUSED ? true : false;
    }
    
    /** 是否已经停止的 */
    public boolean isStopped() {
        return state == STATE_STOPPED ? true : false;
    }
    
    /** 判断文件是否已经全部下载完 */
    public synchronized boolean isOk() {
        return (readBytes >= totalBytes);
    }
    
    // ------------------------------------------------------------ 任务控制
    
    /** 开始下载任务 */
    public synchronized void toStart() {     //开始下载任务加上同步080817 14:01 pigdanx
        if (isLoading()) {
            //System.out.println("正在运行中");
            return;
        } else if (isPaused()) {    // 暂停中的...
            //System.out.println("暂停转为运行...");
            state = STATE_LOADING;
            for (PieceLoader pl : loaders) {
                pl.toContinue();
            }
        } else {
            //System.out.println("直接运行");
            // 如果还从未下载过数据，则完全初始化
            if (!isInit) init0();
            // 检查相应的文件是否存在,确保不会出现重名文件
//            checkAndCreateConfig();//TODO 20080704
            	checkAndCreateConfigAndOverRide(); 
            	state = STATE_LOADING;
                loading();
            
            
            
        }
    }
    
    /** 暂停任务 */
    public void toPause() {
        state = STATE_PAUSED;
    }
    
    /** 停止任务,如果任务已经完成，则重命名文件名*/
    public synchronized void toStop() {
        try {
            if (isLoading() || isPaused()) {
                timer.cancel();
                config.savePieces(tasksAll);
                state = STATE_STOPPED;
                for (PieceLoader pl : loaders) {
                    pl.interrupt();
                }
            }
        } catch (Exception exception) {
        }
    }
    
    /** 删除下载任务 */
    public boolean toDelete() {
        toStop();
        if (fileProcess != null && fileProcess.exists()) {
            fileProcess.delete();
        }
        if (config != null) {
            config.delete();
            TaskManager.getInstance().deleteConfig(config.getId());
        }
        TaskManager.getInstance().removeTask(taskid);
        return true;
    }
    
    /** 处理已经完成的任务 */
    public synchronized void processWhenOk() {
    	
    	config.savePieces(tasksAll);//add by pope 20080807  确保列表存在
    	
    	timer.cancel();
        fileProcess.renameTo(fileSave);
        //TODO 判断 是否要切换
        System.out.println("Downloader config sid:"+config.getSid()+" action:"+config.getAction());
        
        
      
//        TaskManager.getInstance().removeTask(taskid);// pope  删除 xml 中的内容的
        //System.out.println("切片数:" + tasksAll.size());
        //System.out.println("读取数据ReadBytes:" + readBytes);
               
        // 弹出对话框 卸载 confirm,然后安装
       	  	 	     	  
			/*try {//20080716
				
				UnInstall.confirm2Uninstall(fileSave.getName(),config.getAction(),config.getSid());
			} catch (Exception e) {
				e.printStackTrace();
			}*/
        
        //TODO 提示下，下载已经完成
			
			 /* config.delete();//删除Config 文件
		      TaskManager.getInstance().deleteConfig(config.getId());*/ //移到了thread 中执行 
        
//           new Delay2Delete().start();
           
          
  
        
           showIsOk(fileSave.toString()); //提示下载完了
		      	  
    }
    
    
   
    
    // ------------------------------------------------------------ 任务处理
    
    private void showIsOk(String name) {
    	JLabel jlb=new JLabel("文件已经下载完毕，请进行下一步操作");	      
		JOptionPane.showMessageDialog(null, jlb, "下载结束  ",JOptionPane.INFORMATION_MESSAGE);
		
//    	int option =JOptionPane.showConfirmDialog(null,   "文件已经下载完毕，请进行下一步操作",   "下载完毕",JOptionPane.YES_OPTION);   
//	    if(option== JOptionPane.YES_OPTION){
//	    	  System.out.println("删除config 线程启动");
//	    	 new Delay2Delete().start();
//	    }else {
//	    	System.out.println("删除config 线程启动，没有启动");
//	    	
//	    }

		
	}

	/** 将一个新的任务区域(待完成的)添加到列表中，tasks, tasksAll */
    public synchronized void addTask(Piece piece) {
        this.tasksAll.add(piece);
        this.tasks.add(piece);
        config.savePieces(tasksAll);
        //System.out.println("tasksAll.size=" + tasksAll.size());
        //System.out.println("tasks.size=" + tasks.size());
    }
    
    /**
     * 增加已读的字节数,下载线程每次读写完一段数据后都会调用该方法
     * @param readBytes
     */
    public synchronized void growReadBytes(long bytes) {
        readBytesNow += bytes;
        readBytes += bytes;
    }
    
    /** 是否有空的任务线程 */
    public synchronized boolean isFreeLoader() {
        return frees.size() > 0;
    }
    
    /** 添加一个空闲的任务线程 */
    public synchronized void addFreeLoader(PieceLoader pl) {
        frees.add(pl);
    }
    
    /** 移除一个空闲线程 */
    public synchronized void removeFreeLoader(PieceLoader pl) {
        frees.remove(pl);
    }
    
    // ------------------------------------------------------------ 任务状态信息
    
    /** 获取任务的URL下载源地址 */
    public String getURL() {
        return url;
    }
    
    /** 获取下载任务的最终文件保存对象 */
    public File getFileSave() {
        return fileSave;
    }
    
    /** 获取下载中的文件对象 */
    public File getFileProcess() {
        return fileProcess;
    }
    
    /** 获取当次启动后已经读取的字节 */
    public long getReadBytesNow() {
        return readBytesNow;
    }
    
    /** 已经读取的总字节数（包括续传的) */
    public long getReadBytes() {
        return readBytes;
    }
    
    /** 获取总文件长度 */
    public long getTotalBytes() {
        return totalBytes;
    }
    
    /** 获取下载速度 (k/s) */
    public long getSpeed() {
        long sp = 0;
        for (PieceLoader pl : loaders) {
            sp += pl.getSpeed();
        }
        return sp;
    }
    
    // ------------------------------------------------------------ 偏移修正
    private int repairCount;    // 修正次数
    private long offsetTotal;   // 偏移量

    public long getOffsetTotal() {
        return offsetTotal;
    }

    public synchronized void setOffsetTotal(long offsetTotal) {
        this.offsetTotal = offsetTotal;
    }

    public int getRepairCount() {
        return repairCount;
    }

    public synchronized void setRepairCount(int repairCount) {
        this.repairCount = repairCount;
    }
    
    // ------------------------------------------------------------ 定时器
    
    /**
     * 任务定时器，每隔一段时间，这个定时器会把当前下载任务的各个分片(Piece)的状态
     * 信息保存到磁盘文件中，以避免程序出现意外或突然蹦溃而造成的无法断点续传的功能
     */
    private Timer timer;
    
    private class MyTimerTask extends TimerTask {

        @Override
        public void run() {
            try {            
            	 if (isOk()) {  
            		 config.savePieces(tasksAll);
            		 System.out.println("下载完后10s 进行了一次保存");           		 
            		 this.cancel();
            	 } 
            	 config.savePieces(tasksAll);
            } catch (Exception ex) {
                //Elog.log("MyTimerTask");
            }
        }
    }

	public Config getConfig() {
		return config;
	}

	public void setConfig(Config config) {
		this.config = config;
	}
	
	/**
	 * 下载完成后十秒 删除  Config文件
	 * @author zou
	 *
	 */
	private  class Delay2Delete extends Thread {   	
   	 public void run() {
			try {
				Thread.sleep(60000);//30's 后删除文件
				
				
				 TaskManager.getInstance().removeTask(taskid);// pope  删除 xml 中的内容的
				 config.delete();//删除Config 文件
			     TaskManager.getInstance().deleteConfig(config.getId());
			     
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
   	 		}
		}
	
	
    
}
